package com.spring.sso.config.security.filter.oauth2;

import com.spring.sso.common.enums.AuthChannelEnum;
import com.spring.sso.common.enums.AuthEndpointEnum;
import com.spring.sso.config.common.AuthenticationConfig;
import com.spring.sso.config.common.Constant;
import com.spring.sso.config.security.strategy.AuthFilterStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * @author : pangfuzhong
 * @description
 * @date : 2021/10/12 14:26
 */
@Slf4j
public class OAuth2PasswordAuthFilter extends UsernamePasswordAuthenticationFilter implements AuthFilterStrategy {
    private AuthenticationConfig authenticationConfig;
    public OAuth2PasswordAuthFilter(AuthenticationConfig authenticationConfig) {
        this.authenticationConfig = authenticationConfig;
    }

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        return this.match(request, response);
    }

    @Override
    public AuthEndpointEnum getAuthEndpointEnum() {
        return AuthEndpointEnum.WEB_BROWSER;
    }

    @Override
    public AuthChannelEnum getAuthChannelEnum() {
        return AuthChannelEnum.OPEN_PLATFORM;
    }

    @Override
    public String filterUrl() {
        return this.authenticationConfig.getOauthLoginUrl();
    }

    @Override
    public Boolean match(HttpServletRequest request, HttpServletResponse response) {
        String requestUrl = request.getRequestURL().toString();
        if(!StringUtils.contains(requestUrl, this.filterUrl())) {
            return Boolean.FALSE;
        }

        // 登陆方式(PC MOBILE)
        String endPoint = request.getHeader(Constant.ENDPOINT_HEADER);
        if(StringUtils.isBlank(endPoint)) {
            return Boolean.FALSE;
        }

        // 登陆渠道(PASSWORD VERIFICATION)
        String channel = request.getHeader(Constant.CHANNEL_HEADER);
        if(StringUtils.isBlank(channel)) {
            return Boolean.FALSE;
        }

        // 登陆方式合法性
        AuthEndpointEnum authEndpointEnum = AuthEndpointEnum.getByCode(endPoint);
        if(Objects.isNull(authEndpointEnum)) {
            return Boolean.FALSE;
        }

        // 登陆渠道合法性
        AuthChannelEnum authChannelEnum = AuthChannelEnum.getByCode(channel);
        if(Objects.isNull(authChannelEnum)) {
            return Boolean.FALSE;
        }

        // 是否匹配当前过滤器处理
        if(!(authEndpointEnum.equals(this.getAuthEndpointEnum()) && authChannelEnum.equals(this.getAuthChannelEnum()))) {
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals(Constant.GET)) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }
            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
            Authentication authentication = this.getAuthenticationManager().authenticate(authRequest);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            return authentication;
        }
    }
}
